﻿using FastORM.Attribute;
using FastORM.Exception;
using FastORM.Mapping;
using FastUtil.Convert;
using System;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
using System.Text;

namespace FastORM.Entity
{
    public class MapperEntity
    {
        public Type EnityType { set; get; }
        public string TableName { set; get; }
        public string KeyName { set; get; }
        public List<PropertyInfo> PropertyList { set; get; }
        public Dictionary<PropertyInfo, string> ColumnMapDic { set; get; }
        public Dictionary<string, EmitEntity> EmitDic { set; get; }
        public List<string> IgnoreFieldList { set; get; }

        public MapperEntity(Type EnityType)
        {
            this.EnityType = EnityType;
            IgnoreFieldList = new List<string>();
            ColumnMapDic = new Dictionary<PropertyInfo, string>();
            EmitDic = new Dictionary<string, EmitEntity>();
        }

        public void ScanSelf()
        {
            PropertyList = StructConvert.ConvertArrayToList(EnityType.GetProperties());
            //获取映射表名
            Table TableAttribute = (Table)EnityType.GetCustomAttribute(typeof(Table));
            if (TableAttribute == null)
            {
                TableName = EnityType.Name;
            }
            else
            {
                TableName = TableAttribute.Name;
                if (string.IsNullOrEmpty(TableName))
                {
                    throw new NoDefineTableNameException(string.Format("实体映射类{0}声明了Table注解，未指定表名称", EnityType.FullName));
                }
            }
            //获取映射主键名
            List<PropertyInfo> KeyPropertyInfoList = PropertyList.FindAll((PropertyInfo p) => p.GetCustomAttribute(typeof(Key)) != null);
            if (KeyPropertyInfoList.Count == 0)
            {
                throw new NoDefineKeyAttributeException(string.Format("实体映射类{0}未指定主键字段", EnityType.FullName));
            }
            else if (KeyPropertyInfoList.Count == 1)
            {
                PropertyInfo KeyPropertyInfo = KeyPropertyInfoList[0];
                Column ColumnAttribute = (Column)KeyPropertyInfo.GetCustomAttribute(typeof(Column));
                if (ColumnAttribute == null)
                {
                    KeyName = KeyPropertyInfo.Name;
                }
                else
                {
                    KeyName = ColumnAttribute.Name;
                    if (string.IsNullOrEmpty(KeyName))
                    {
                        throw new NoDefineColumnNameException(string.Format("实体映射类{0}中的{1}字段声明了Column注解，未指定列名称", EnityType.FullName, KeyPropertyInfo.Name));
                    }
                }
            }
            else
            {
                throw new RepeatKeyAttributeException(string.Format("实体映射类{0}定义不止一个主键字段", EnityType.FullName));
            }
            //获取映射列
            foreach (PropertyInfo PropertyItem in PropertyList)
            {
                Ignore IgnoreAttribute = (Ignore)PropertyItem.GetCustomAttribute(typeof(Ignore));
                if (IgnoreAttribute == null)
                {
                    string ColumnName = string.Empty;
                    Column ColumnAttribute = (Column)PropertyItem.GetCustomAttribute(typeof(Column));
                    if (ColumnAttribute != null)
                    {
                        ColumnName = ColumnAttribute.Name;
                    }
                    else
                    {
                        ColumnName = PropertyItem.Name;
                    }

                    if (!string.IsNullOrEmpty(ColumnName))
                    {
                        if (ColumnMapDic.ContainsValue(ColumnName))
                        {
                            throw new RepeatColumnException(string.Format("实体映射类{0}定义的属性名称或Column注解中有重复的列{1}", EnityType.FullName, ColumnName));
                        }
                        else
                        {
                            ColumnMapDic.Add(PropertyItem, ColumnName);
                        }
                        //使用Emit创建set，get方法的委托
                        EmitEntity emit = new EmitEntity();
                        emit.EmitSetter = EmitMapping.EmitSetter(EnityType, PropertyItem.Name);
                        emit.EmitGetter = EmitMapping.EmitGetter(EnityType, PropertyItem.Name);
                        EmitDic.Add(PropertyItem.Name, emit);
                    }
                    else
                    {
                        throw new NoDefineColumnNameException(string.Format("实体映射类{0}中的{1}字段声明了Column注解，未指定列名称", EnityType.FullName, PropertyItem.Name));
                    }
                }
                else
                {
                    IgnoreFieldList.Add(PropertyItem.Name);
                }
            }

        }

        public T Map<T>(DataRow Row)
        {
            //获取空构造函数
            ConstructorInfo ct = EnityType.GetConstructor(System.Type.EmptyTypes);
            //new 对象
            T Entity = (T)ct.Invoke(null);
            foreach (var item in ColumnMapDic)
            {
                if (Row.Table.Columns.Contains(item.Value))
                {
                    if (Convert.IsDBNull(Row[item.Value]) && EmitDic.ContainsKey(item.Key.Name))
                    {
                        var FieldValue = item.Key.PropertyType.IsValueType ? Activator.CreateInstance(item.Key.PropertyType) : null;
                        EmitDic[item.Key.Name].EmitSetter(Entity, FieldValue);
                    }
                    else
                    {
                        EmitDic[item.Key.Name].EmitSetter(Entity, Row[item.Value]);
                    }
                }
            }
            return Entity;
        }

        public object GetKeyValue<T>(T Entity)
        {
            string PropertyName = string.Empty;
            foreach (var item in ColumnMapDic)
            {
                if (item.Value==KeyName)
                {
                    PropertyName = item.Key.Name;
                    break;
                }
            }
            return EmitDic[PropertyName].EmitGetter(Entity);
        }

    }
}
